import socket,time
import ssl,json
from threading import Thread
class Client():
    def __init__(self, socket, address):
        self.socket = socket
        self.ip=address[0]
        self.port=address[1]
        self.client_id = "%s-%s" % (self.ip,self.port)
        self.client_name=''

    def close(self):
        try:
            self.socket.close()
            return True
        except:
            return False
#群发消息
def broadcast_message(json_data, message, sender_client):
    for id in clients.keys():
        if id != sender_client.client_id:
            if json_data['type']=='text':
                clients[id].socket.send(json.dumps(json_data).encode('utf-8'))
                print(f"broadcast message: {json_data}")
            else:
                clients[id].socket.send(json.dumps(json_data).encode('utf-8'))
                print(f"broadcast message: {json_data}")
                time.sleep(0.1)
                clients[id].socket.send(message)
                print(f"broadcast message: {message}")


def broadcast_online_user():
    data={'type' : 'memberList',
          }
    #记录在线人数
    name_ip_list=[]
    for i in user_names.keys():
        name_ip_list.append(user_names[i])
        name_ip_list.append(ip[i])
    data.update({'members':name_ip_list})
    #逐个发送
    for i in clients.values():
        i.socket.send(json.dumps(data).encode('utf-8'))
        print(f"broadcast online user: {data}")

#更新用户名字
def renewName(json_data,client):
    user_names.update({client.client_id:json_data['newName']})

#关闭客户端
def close_client(c):
    print("Off Line:",c.client_id)
    clients.pop(c.client_id)
    user_names.pop(c.client_id)
    ip.pop(c.client_id)
    c.close()

#处理每个client的数据收发socket
def handle_client(client):
    register=False
    while True:
        if register==True: #如果用户已经上线, 再开启1s定时
            client.socket.settimeout(1) #recv最多等待1s, 否则将去检测是否掉线
        try:
            data = client.socket.recv(4096).decode('utf-8')
            if data:
                json_data=json.loads(data)
                print(f"From client={client.ip} {client.port}, receive json data={json_data}")
                message=b''
                #接收消息
                try:
                    if json_data['type']=='text': #传文本
                        message=json_data['textContent']
                    elif json_data['type']=='image': #传照片
                        ImageSize=json_data['imageSize']
                        while len(message)<ImageSize:
                            message+=client.socket.recv(1024)
                    elif json_data['type']=='file': #传输文件
                        FileSize=json_data['fileSize']
                        while len(message)<FileSize:
                            message+=client.socket.recv(1024)
                    elif json_data['type']=='userName': #记录用户名字, 并广播用户名字
                        renewName(json_data,client)
                        ip.update({client.client_id:json_data['ip']})
                        broadcast_online_user()
                        register=True
                        continue
                except Exception as e:
                    print(f"Error hadle client: {str(e)}")
                if message=='quit':
                    close_client(client)
                    broadcast_online_user() #重新广播用户名字
                    return
                #群发消息
                broadcast_message(json_data,message, client)

        except ConnectionResetError as e: #用户掉线, 注销
            print("Error: Remote host forcibly closed the connection.")
            close_client(client)
            broadcast_online_user() #重新广播用户名字
            return
        except Exception as e: #其他意外情况
            try:#尝试检测用户是否在线
                client.socket.getsockname()
                continue
            except:
                close_client(client)
                broadcast_online_user() #重新广播用户名字
                return

if __name__ == "__main__":
    clients = {}
    user_names = {}
    ip = {}
    port=12345

    # 用于监听的socket
    service = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    service.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    service.bind(('0.0.0.0', port))
    service.listen(10)  # 最多10个用户排队
    print("Server is listening on port", port)
    # 等待client连接
    while True:
        #返回值: 用于传输数据的socket
        client, client_addr = service.accept()
        print(f"client ={client_addr}")
        #将该socket进行适当封装
        client = Client(client, client_addr)
        clients[client.client_id]=client
        #在多线程中运行socket的收发
        client_thread = Thread(target=handle_client, args=(client,))
        client_thread.start()
